package xconsole

import (
	"flag"
	"fmt"
	"strings"

	"gitee.com/xiaoyutab/xgotool/xnum"
	"gitee.com/xiaoyutab/xgotool/xstring"
)

// 创建数据库增删改查模型
// 包含gorm模型、data层模型、控制器模型
func mkGRUB(help bool) string {
	if help {
		return "生成data层数据操作规划"
	}
	dns := ""
	table := ""
	file_model := ""
	file_data := ""
	file_controller := ""
	dbs := ""
	delete_field := ""
	cache := false
	flag.StringVar(&dns, "dns", "admin:admin@tcp(127.0.0.1:3306)/log_analysis?charset=utf8", "数据库DNS连接地址")
	flag.StringVar(&table, "table", "logs", "数据库表名称，目前仅支持单表生成")
	flag.StringVar(&dbs, "dbs", "utils.DB", "项目中的数据库连接字符串")
	flag.StringVar(&delete_field, "d-field", "", "删除字段，空表示直接走物理删除")
	flag.StringVar(&file_model, "model", "/tmp/model", "模型文件存放位置（需保证此目录存在）")
	flag.StringVar(&file_data, "data", "/tmp/server/logs", "数据操作文件存放位置【文件名为`table_xxxx`，所以多个表的操作可以放到一块去】（需保证此目录存在）")
	flag.StringVar(&file_controller, "controller", "/tmp/controller/logs", "控制器对应的文件位置（需保证此目录存在）")
	flag.BoolVar(&cache, "cache", false, "是否使用cache缓存")
	flag.Parse()
	con := connect(dns).setTable(table).setDBS(dbs).setDeletedField(delete_field)
	if con.Error != nil {
		panic(con.Error.Error())
	}
	file_model = strings.TrimRight(file_model, "/")
	file_data = strings.TrimRight(file_data, "/")
	file_controller = strings.TrimRight(file_controller, "/")
	// 生成模型
	model := strings.Split(file_model, "/")[len(strings.Split(file_model, "/"))-1]
	mkmodels(con, model, file_model, "")
	// 生成data层数据
	mkdata(con, file_data, model, cache)
	// 生成控制器层
	server := strings.Split(file_data, "/")[len(strings.Split(file_data, "/"))-1]
	mkcontroller(con, file_controller, server, model)
	controller := strings.Split(file_controller, "/")[len(strings.Split(file_controller, "/"))-1] + "."
	// 输出最后的帮助信息
	fmtFunction(fmt.Sprintf("%s/%s.go", file_model, con.Table),
		fmt.Sprintf("%s/%s_create.go", file_data, con.Table),
		fmt.Sprintf("%s/%s_delete.go", file_data, con.Table),
		fmt.Sprintf("%s/%s_info.go", file_data, con.Table),
		fmt.Sprintf("%s/%s_list.go", file_data, con.Table),
		fmt.Sprintf("%s/%s_create.go", file_controller, con.Table),
		fmt.Sprintf("%s/%s_delete.go", file_controller, con.Table),
		fmt.Sprintf("%s/%s_info.go", file_controller, con.Table),
		fmt.Sprintf("%s/%s_list.go", file_controller, con.Table),
	)
	fmt.Println("执行完成以后请再添加以下路由，以便提供访问：")
	fmt.Printf("// 使用router来替代路由注入变量\nrouter.GET(\"%s/info\",xgin.Convert(%sInfo))\nrouter.GET(\"%s/create\",xgin.Convert(%sCreate))\nrouter.GET(\"%s/delete\",xgin.Convert(%sDelete))\nrouter.GET(\"%s/list\",xgin.Convert(%sList))\n",
		con.Table, controller+xstring.CamelCase(con.Table),
		con.Table, controller+xstring.CamelCase(con.Table),
		con.Table, controller+xstring.CamelCase(con.Table),
		con.Table, controller+xstring.CamelCase(con.Table),
	)
	return ""
}

// 生成控制器层数据
func mkcontroller(con *gorm_config, file_controller, server, model string) {
	data := strings.Split(file_controller, "/")[len(strings.Split(file_controller, "/"))-1]
	server += "."
	pktype := "int"
	for i := 0; i < len(con.Field); i++ {
		if con.Field[i].Field == "id" {
			pktype = getFiledType(con.Field[i])
		}
	}
	// 查询单条记录
	info_string := "package " + data
	// 因FMT工具未能自动引入对应依赖，所以此处需手动进行注入依赖
	info_string += `
	import (
		"encoding/json"
	
		"gitee.com/xiaoyutab/xgotool/xgin"
		"gitee.com/xiaoyutab/xgotool/xstring"
		"github.com/gin-gonic/gin"
	)
`
	info_string += "\n\n// 根据ID获取数据详情接口"
	info_string += "\n//\n//\tGET\t/xxxx/" + data + "/info"
	info_string += "\nfunc " + xstring.CamelCase(con.Table) + "Info(c *gin.Context) *xgin.Response {"
	info_string += "\n\ttype temp_struct struct {"
	info_string += "\n\t\tId\t"
	if pktype == "string" {
		info_string += "string\t`json:\"id\" form:\"id\" xml:\"id\"`\t//"
	} else {
		info_string += "json.Number\t`json:\"id\" form:\"id\" xml:\"id\"`\t//"
	}
	info_string += "\n\t}"
	if pktype != "string" {
		info_string += "\n\ttype temp_struct_used struct {"
		info_string += "\n\t\tId\t"
		info_string += pktype + "\t`json:\"id\" form:\"id\" xml:\"id\"`\t//"
		info_string += "\n\t}"
	}
	info_string += "\n\ttemp := temp_struct{}"
	info_string += "\n\tif err := xgin.Bind(c, &temp); err != nil {"
	info_string += "\n\t\treturn xgin.Resp().Error(err)"
	info_string += "\n\t}"
	if pktype == "string" {
		info_string += "\n\tparam := temp"
	} else {
		info_string += "\n\tparam := temp_struct_used{}"
		info_string += "\n\tparam.Id = xstring.J" + xstring.CamelCase(pktype) + "(temp.Id)"
	}
	info_string += "\n\tinf, err := " + server + xstring.CamelCase(con.Table) + "Info(param.Id)"
	info_string += "\n\tif err != nil {\n\t\treturn xgin.Resp().Error(err)\n\t}"
	info_string += "\n\treturn xgin.Resp().SuccessAny(inf)\n}"
	writeFile(file_controller+"/"+con.Table+"_info.go", info_string)
	// 查询分页列表数据
	list_string := "package " + data
	// 因FMT工具未能自动引入对应依赖，所以此处需手动进行注入依赖
	list_string += `
	import (
		"gitee.com/xiaoyutab/xgotool/xgin"
		"github.com/gin-gonic/gin"
	)
`
	list_string += "\n\n// 根据page、limit获取列表数据"
	list_string += "\n//\n//\tGET\t/xxxx/" + data + "/list"
	list_string += "\nfunc " + xstring.CamelCase(con.Table) + "List(c *gin.Context) *xgin.Response {"
	list_string += "\n\t// 需要使用c.GetInt(\"offset\")和c.GetInt(\"limit\")来获取分页数据，所以请使用中间件注入这两个参数"
	list_string += "\n\tcount, list, err := " + server + xstring.CamelCase(con.Table) + "List(c.GetInt(\"offset\"), c.GetInt(\"limit\"))"
	list_string += "\n\tif err != nil {\n\t\treturn xgin.Resp().Error(err)\n\t}"
	list_string += "\n\treturn xgin.Resp().Page(c, list, count)\n}"
	writeFile(file_controller+"/"+con.Table+"_list.go", list_string)
	// 删除数据
	delete_string := "package " + data
	// 因FMT工具未能自动引入对应依赖，所以此处需手动进行注入依赖
	delete_string += `
	import (
		"encoding/json"
	
		"gitee.com/xiaoyutab/xgotool/xgin"
		"gitee.com/xiaoyutab/xgotool/xstring"
		"github.com/gin-gonic/gin"
	)
`
	delete_string += "\n\n// 根据ID获取数据详情接口"
	delete_string += "\n//\n//\tGET\t/xxxx/" + data + "/delete"
	delete_string += "\nfunc " + xstring.CamelCase(con.Table) + "Delete(c *gin.Context) *xgin.Response {"
	delete_string += "\n\ttype temp_struct struct {"
	delete_string += "\n\t\tId\t"
	if pktype == "string" {
		delete_string += "string\t`json:\"id\" form:\"id\" xml:\"id\"`\t//"
	} else {
		delete_string += "json.Number\t`json:\"id\" form:\"id\" xml:\"id\"`\t//"
	}
	delete_string += "\n\t}"
	if pktype != "string" {
		delete_string += "\n\ttype temp_struct_used struct {"
		delete_string += "\n\t\tId\t"
		delete_string += pktype + "\t`json:\"id\" form:\"id\" xml:\"id\"`\t//"
		delete_string += "\n\t}"
	}
	delete_string += "\n\ttemp := temp_struct{}"
	delete_string += "\n\tif err := xgin.Bind(c, &temp); err != nil {"
	delete_string += "\n\t\treturn xgin.Resp().Error(err)"
	delete_string += "\n\t}"
	if pktype == "string" {
		delete_string += "\n\tparam := temp"
	} else {
		delete_string += "\n\tparam := temp_struct_used{}"
		delete_string += "\n\tparam.Id = xstring.J" + xstring.CamelCase(pktype) + "(temp.Id)"
	}
	delete_string += "\n\terr := " + server + xstring.CamelCase(con.Table) + "Delete(param.Id)"
	delete_string += "\n\tif err != nil {\n\t\treturn xgin.Resp().Error(err)\n\t}"
	delete_string += "\n\treturn xgin.Resp().SuccessEmpty()\n}"
	writeFile(file_controller+"/"+con.Table+"_delete.go", delete_string)
	// 创建操作
	create_string := "package " + data
	// 因FMT工具未能自动引入对应依赖，所以此处需手动进行注入依赖
	create_string += `
	import (
		"encoding/json"
	
		"gitee.com/xiaoyutab/xgotool/xgin"
		"gitee.com/xiaoyutab/xgotool/xstring"
		"github.com/gin-gonic/gin"
	)
`
	create_string += "\n\n// 根据传入数据进行数据保存"
	create_string += "\n//\n//\tGET\t/xxxx/" + data + "/create"
	create_string += "\nfunc " + xstring.CamelCase(con.Table) + "Create(c *gin.Context) *xgin.Response {"
	create_string += "\n\t// 用户传入数据，使用json.Number用于兼容前端传入的数字，后续使用xstring.Jxxx来转换，避免出现无法存储的问题"
	create_string += "\n\ttype temp_struct struct {"
	for i := 0; i < len(con.Field); i++ {
		temp := "\n\t\t" + xstring.CamelCase(con.Field[i].Field)
		if getFiledType(con.Field[i]) == "string" {
			temp += "\tstring"
		} else {
			temp += "\tjson.Number"
		}
		temp += "\t`json:\"" + con.Field[i].Field + "\" form:\"" + con.Field[i].Field + "\" xml:\"" + con.Field[i].Field + "\"`\t// " + con.Field[i].Comment
		create_string += temp
	}
	create_string += "\n\t}"
	create_string += "\n\t// 数据绑定、提取"
	create_string += "\n\ttemp := temp_struct{}"
	create_string += "\n\tif err := xgin.Bind(c, &temp); err != nil {"
	create_string += "\n\t\treturn xgin.Resp().Error(err)"
	create_string += "\n\t}"
	create_string += "\n\t// 数据转化，转化为数据库中可以存储的结构信息"
	create_string += "\n\tparam := " + model + "." + xstring.CamelCase(con.Table) + "{}"
	for i := 0; i < len(con.Field); i++ {
		if getFiledType(con.Field[i]) == "string" {
			create_string += "\n\tparam." + xstring.CamelCase(con.Field[i].Field) + " = temp." + xstring.CamelCase(con.Field[i].Field)
		} else {
			create_string += "\n\tparam." + xstring.CamelCase(con.Field[i].Field) + " = xstring.J" + xstring.CamelCase(getFiledType(con.Field[i])) + "(temp." + xstring.CamelCase(con.Field[i].Field) + ")"
		}
	}
	create_string += "\n\t// 数据转化完成，格式化、参数验证等逻辑请在此处进行填写"
	create_string += "\n\t// 数据存储"
	create_string += "\n\terr := " + server + xstring.CamelCase(con.Table) + "Create(&param)"
	create_string += "\n\tif err != nil {\n\t\treturn xgin.Resp().Error(err)\n\t}"
	create_string += "\n\treturn xgin.Resp().SuccessEmpty()\n}"
	writeFile(file_controller+"/"+con.Table+"_create.go", create_string)
}

// 生成data层数据
//
//	con			连贯操作结构体配置
//	file_data	文件存放目录
//	model		模块名称
//	cache		是否需要写入缓存
func mkdata(con *gorm_config, file_data, model string, cache bool) {
	data := strings.Split(file_data, "/")[len(strings.Split(file_data, "/"))-1]
	if model == data {
		model = ""
	} else {
		model += "."
	}
	pktype := "int"
	for i := 0; i < len(con.Field); i++ {
		if con.Field[i].Field == "id" {
			pktype = getFiledType(con.Field[i])
		}
	}
	// 创建
	create_string := "package " + data
	// 因FMT工具未能自动引入对应依赖，所以此处需手动进行注入依赖
	create_string += `
	import (
		"errors"
		"time"
	
		`
	if cache {
		create_string += "\"gitee.com/xiaoyutab/xgotool/optional/xcache\"\n"
	}
	create_string += "\"gitee.com/xiaoyutab/xgotool/optional/xlog\"\n\t)"
	create_string += "\n\n// 存储数据信息"
	create_string += "\n//\n//\tc\t存储的 " + con.Table + con.TableInfo.Comment + " 表内容"
	create_string += "\nfunc " + xstring.CamelCase(con.Table) + "Create(c *" + model + xstring.CamelCase(con.Table) + ") error {"
	create_string += "\n\t// 数据库连接检测"
	create_string += "\n\tif(" + con.DBS + " == nil ){\n\t\treturn xerror.New(\"数据库未连接\")\n\t}"
	create_string += "\n\t// 时间变量检测"
	for i := 0; i < len(con.Field); i++ {
		if xnum.InArray(con.Field[i].Field, []string{"deleted_at", "delete_time"}) {
			create_string += "\n\tif c." + xstring.CamelCase(con.Field[i].Field) + " == \"\"{"
			create_string += "\n\t\tc." + xstring.CamelCase(con.Field[i].Field) + " = \"1970-01-01 08:00:00\"\n\t}"
		} else if xnum.InArray(con.Field[i].Type, []string{"timestamp", "datetime"}) {
			create_string += "\n\tif c." + xstring.CamelCase(con.Field[i].Field) + " == \"\"{"
			create_string += "\n\t\tc." + xstring.CamelCase(con.Field[i].Field) + " = time.Now().Format(time.DateTime)\n\t}"
		} else if con.Field[i].Type == "time" {
			create_string += "\n\tif c." + xstring.CamelCase(con.Field[i].Field) + " == \"\"{"
			create_string += "\n\t\tc." + xstring.CamelCase(con.Field[i].Field) + " = time.Now().Format(time.TimeOnly)\n\t}"
		} else if con.Field[i].Type == "date" {
			create_string += "\n\tif c." + xstring.CamelCase(con.Field[i].Field) + " == \"\"{"
			create_string += "\n\t\tc." + xstring.CamelCase(con.Field[i].Field) + " = time.Now().Format(time.DateOnly)\n\t}"
		}
	}
	create_string += "\n\t// 数据保存"
	create_string += "\n\tdb := " + con.DBS + ".Table(\"" + con.Table + "\").Save(c)"
	create_string += "\n\tif db.Error != nil {\n\t\treturn xlog.AE(\"表 " + con.Table + " 的数据保存失败\",db.Error)\n\t}"
	if cache {
		create_string += "\n\t// 更新写入缓存"
		create_string += "\n\txcache.SetStruct(xcache.Key(\"mkdata." + data + "." + con.Table + ".info\", c.Id),c)"
	}
	create_string += "\n\t// 返回结果"
	create_string += "\n\treturn nil\n}"
	writeFile(file_data+"/"+con.Table+"_create.go", create_string)
	// 查询
	find_string := "package " + data
	// 因FMT工具未能自动引入对应依赖，所以此处需手动进行注入依赖
	find_string += `
	import (
		"errors"
	
		"gitee.com/xiaoyutab/xgotool/optional/xcache"
		"gitee.com/xiaoyutab/xgotool/optional/xlog"
	)
`
	find_string += "\n\n// 查询数据信息"
	find_string += "\n//\n//\tid\t查询的 " + con.Table + con.TableInfo.Comment + " 表主键ID"
	find_string += "\nfunc " + xstring.CamelCase(con.Table) + "Info(id " + pktype + ") (*" + model + xstring.CamelCase(con.Table) + ", error) {"
	find_string += "\n\t// 数据库连接检测"
	find_string += "\n\tif(" + con.DBS + " == nil ){\n\t\treturn nil, xerror.New(\"数据库未连接\")\n\t}"
	find_string += "\n\t// 缓存检测"
	find_string += "\n\tcache_key := xcache.Key(\"mkdata." + data + "." + con.Table + ".info\", id)"
	find_string += "\n\tmodels := " + model + xstring.CamelCase(con.Table) + "{}"
	find_string += "\n\tif xcache.Exists(cache_key){"
	find_string += "\n\t\tif err := xcache.GetStruct(cache_key,&models);err == nil {\n\t\t\treturn &models, nil\n\t\t}\n\t}"
	find_string += "\n\t// 从数据库中进行读取"
	find_string += "\n\terr := " + con.DBS + ".Table(\"" + con.Table + "\").Where(\"id\", id)"
	if con.DeletedWhere != "" {
		// 如果是字段删除的话，需要追加上字段额条件信息
		switch con.DeletedWhere {
		case "int":
			find_string += ".Where(\"" + con.DeletedField + "\", 0)"
		case "date":
			find_string += ".Where(\"" + con.DeletedField + "\", \"1970-01-01\")"
		case "time":
			find_string += ".Where(\"" + con.DeletedField + "\", \"08:00:00\")"
		case "datetime":
			find_string += ".Where(\"" + con.DeletedField + "\", \"1970-01-01 08:00:00\")"
		}
	}
	find_string += ".Find(&models).Error"
	find_string += "\n\tif err != nil {\n\t\treturn nil, xlog.AE(\"表 " + con.Table + " 的数据查询失败\", err)\n\t}"
	find_string += "\n\tif models.Id == 0 {\n\t\treturn nil, xerror.New(\"数据未找到\")\n\t}"
	if cache {
		find_string += "\n\t// 写入缓存"
		find_string += "\n\txcache.SetStruct(cache_key, models)"
	}
	find_string += "\n\t// 返回结果信息"
	find_string += "\n\treturn &models, nil"
	find_string += "\n}"
	writeFile(file_data+"/"+con.Table+"_info.go", find_string)
	// 删除操作
	deleted_string := "package " + data
	// 因FMT工具未能自动引入对应依赖，所以此处需手动进行注入依赖
	deleted_string += `
	import (
		"errors"
	
		"gitee.com/xiaoyutab/xgotool/optional/xcache"
		"gitee.com/xiaoyutab/xgotool/optional/xlog"
	)
`
	deleted_string += "\n\n// 查询数据信息"
	deleted_string += "\n//\n//\tid\t查询的 " + con.Table + con.TableInfo.Comment + " 表主键ID"
	deleted_string += "\nfunc " + xstring.CamelCase(con.Table) + "Delete(id " + pktype + ") error {"
	deleted_string += "\n\tif id <= 0 {\n\t\treturn xerror.New(\"参数查询错误\")\n\t}"
	deleted_string += "\n\t// 数据库连接检测"
	deleted_string += "\n\tif(" + con.DBS + " == nil ){\n\t\treturn xerror.New(\"数据库未连接\")\n\t}"
	deleted_string += "\n\t// 执行删除操作"
	deleted_string += "\n\tdb := " + con.DBS + ".Table(\"" + con.Table + "\").Where(\"id\", id)"
	if con.DeletedWhere != "" {
		// 字段逻辑删除
		switch con.DeletedWhere {
		case "int":
			deleted_string += ".Update(\"" + con.DeletedField + "\", 1)"
		case "date":
			deleted_string += ".Update(\"" + con.DeletedField + "\", time.Now().Format(time.DateOnly))"
		case "time":
			deleted_string += ".Update(\"" + con.DeletedField + "\", time.Now().Format(time.TimeOnly))"
		case "datetime":
			deleted_string += ".Update(\"" + con.DeletedField + "\", time.Now().Format(time.DateTime))"
		}
	} else {
		// 物理删除
		deleted_string += ".Delete(&" + model + xstring.CamelCase(con.Table) + "{})"
	}
	deleted_string += "\n\t// 错误判断"
	deleted_string += "\n\tif db.Error != nil {\n\t\treturn xlog.AE(\"表 " + con.Table + " 的数据查询失败\", db.Error)\n\t}"
	deleted_string += "\n\t// 空条数判断"
	deleted_string += "\n\tif db.RowsAffected <= 0 {\n\t\treturn xerror.New(\"数据未找到\")\n\t}"
	deleted_string += "\n\t// 缓存清理"
	deleted_string += "\n\txcache.Remove(xcache.Key(\"mkdata." + data + "." + con.Table + ".info\", id))"
	deleted_string += "\n\treturn nil\n}"
	writeFile(file_data+"/"+con.Table+"_delete.go", deleted_string)
	// 列表服务
	list_string := "package " + data
	// 因FMT工具未能自动引入对应依赖，所以此处需手动进行注入依赖
	list_string += `
	import (
		"errors"
	
		"gitee.com/xiaoyutab/xgotool/optional/xlog"
	)
`
	list_string += "\n\n// 查询数据信息"
	list_string += "\n//\n//\toffset\t跳过条数"
	list_string += "\n//\tlimit\t查询条数"
	list_string += "\nfunc " + xstring.CamelCase(con.Table) + "List(offset, limit  int) (int64, []" + model + xstring.CamelCase(con.Table) + ", error) {"
	list_string += "\n\tif offset <= 0 {\n\t\toffset = 0\n\t}"
	list_string += "\n\tif limit <= 0 {\n\t\tlimit = 20\n\t}"
	list_string += "\n\t// 数据库连接检测"
	list_string += "\n\tif(" + con.DBS + " == nil ){\n\t\treturn 0, nil, xerror.New(\"数据库未连接\")\n\t}"
	list_string += "\n\t// 执行查询操作"
	list_string += "\n\tdb := " + con.DBS + ".Table(\"" + con.Table + "\")"
	if con.DeletedWhere != "" {
		// 字段逻辑删除
		switch con.DeletedWhere {
		case "int":
			list_string += ".\n\t\tWhere(\"" + con.DeletedField + "\", 0)"
		case "date":
			list_string += ".\n\t\tWhere(\"" + con.DeletedField + "\", \"1970-01-01\")"
		case "time":
			list_string += ".\n\t\tWhere(\"" + con.DeletedField + "\", \"08:00:00\")"
		case "datetime":
			list_string += ".\n\t\tWhere(\"" + con.DeletedField + "\", \"1970-01-01 08:00:00\")"
		}
	}
	list_string += ".\n\t\tOrder(\"id DESC\")"
	list_string += "\n\tvar count int64"
	list_string += "\n\terr := db.Count(&count).Error"
	list_string += "\n\tif err != nil {\n\t\treturn 0, nil, xlog.AE(\"表 " + con.Table + " 的总条数查询失败\", err)\n\t}"
	list_string += "\n\tmodel := []" + model + xstring.CamelCase(con.Table) + "{}"
	list_string += "\n\terr = db.Offset(offset).Limit(limit).Find(&model).Error"
	list_string += "\n\tif err != nil {\n\t\treturn 0, nil, xlog.AE(\"表 " + con.Table + " 的数据列表查询失败\", err)\n\t}"
	list_string += "\n\treturn count, model, nil\n}"
	writeFile(file_data+"/"+con.Table+"_list.go", list_string)
}
